namespace PickGold.Charting
{
    using System;
    using System.Globalization;
    using System.Security.Permissions;
    using System.Web;

    [AspNetHostingPermission(SecurityAction.LinkDemand, Level=AspNetHostingPermissionLevel.Minimal), AspNetHostingPermission(SecurityAction.InheritanceDemand, Level=AspNetHostingPermissionLevel.Minimal)]
    public class StatisticFormula
    {
        private DataFormula _formulaData;
        private string _tempOutputSeriesName = "Statistical Analyses Formula Temporary Output Series 2552003";

        internal StatisticFormula(DataFormula formulaData)
        {
            this._formulaData = formulaData;
        }

        public AnovaResult Anova(double probability, string inputSeriesNames)
        {
            if (inputSeriesNames == null)
            {
                throw new ArgumentNullException("inputSeriesNames");
            }
            AnovaResult result = new AnovaResult();
            string parameters = probability.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            try
            {
                this._formulaData.Formula("Anova", parameters, inputSeriesNames, this._tempOutputSeriesName);
                DataPointCollection points = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points;
                result.sumOfSquaresBetweenGroups = points[0].YValues[0];
                result.sumOfSquaresWithinGroups = points[1].YValues[0];
                result.sumOfSquaresTotal = points[2].YValues[0];
                result.degreeOfFreedomBetweenGroups = points[3].YValues[0];
                result.degreeOfFreedomWithinGroups = points[4].YValues[0];
                result.degreeOfFreedomTotal = points[5].YValues[0];
                result.meanSquareVarianceBetweenGroups = points[6].YValues[0];
                result.meanSquareVarianceWithinGroups = points[7].YValues[0];
                result.fRatio = points[8].YValues[0];
                result.fCriticalValue = points[9].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return result;
        }

        public double BetaFunction(double m, double n)
        {
            if ((m == 0.0) && (n == 0.0))
            {
                return double.NaN;
            }
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            string parameters = m.ToString(CultureInfo.InvariantCulture) + "," + n.ToString(CultureInfo.InvariantCulture);
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("BetaFunction", parameters, this._tempOutputSeriesName, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public double Correlation(string firstInputSeriesName, string secondInputSeriesName)
        {
            if (firstInputSeriesName == null)
            {
                throw new ArgumentNullException("firstInputSeriesName");
            }
            if (secondInputSeriesName == null)
            {
                throw new ArgumentNullException("secondInputSeriesName");
            }
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            string inputSeries = firstInputSeriesName + "," + secondInputSeriesName;
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("Correlation", "", inputSeries, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public double Covariance(string firstInputSeriesName, string secondInputSeriesName)
        {
            if (firstInputSeriesName == null)
            {
                throw new ArgumentNullException("firstInputSeriesName");
            }
            if (secondInputSeriesName == null)
            {
                throw new ArgumentNullException("secondInputSeriesName");
            }
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            string inputSeries = firstInputSeriesName.ToString(CultureInfo.InvariantCulture) + "," + secondInputSeriesName.ToString(CultureInfo.InvariantCulture);
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("Covariance", "", inputSeries, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public double FDistribution(double value, int firstDegreeOfFreedom, int secondDegreeOfFreedom)
        {
            string parameters = (value.ToString(CultureInfo.InvariantCulture) + "," + firstDegreeOfFreedom.ToString(CultureInfo.InvariantCulture)) + "," + secondDegreeOfFreedom.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("FDistribution", parameters, this._tempOutputSeriesName, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public FTestResult FTest(double probability, string firstInputSeriesName, string secondInputSeriesName)
        {
            if (firstInputSeriesName == null)
            {
                throw new ArgumentNullException("firstInputSeriesName");
            }
            if (secondInputSeriesName == null)
            {
                throw new ArgumentNullException("secondInputSeriesName");
            }
            FTestResult result = new FTestResult();
            string parameters = probability.ToString(CultureInfo.InvariantCulture);
            string inputSeries = firstInputSeriesName.ToString(CultureInfo.InvariantCulture) + "," + secondInputSeriesName.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            this.RemoveEmptyPoints(firstInputSeriesName);
            this.RemoveEmptyPoints(secondInputSeriesName);
            try
            {
                this._formulaData.Formula("FTest", parameters, inputSeries, this._tempOutputSeriesName);
                DataPointCollection points = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points;
                result.firstSeriesMean = points[0].YValues[0];
                result.secondSeriesMean = points[1].YValues[0];
                result.firstSeriesVariance = points[2].YValues[0];
                result.secondSeriesVariance = points[3].YValues[0];
                result.fValue = points[4].YValues[0];
                result.probabilityFOneTail = points[5].YValues[0];
                result.fCriticalValueOneTail = points[6].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return result;
        }

        public double GammaFunction(double value)
        {
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            string parameters = value.ToString(CultureInfo.InvariantCulture);
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("GammaFunction", parameters, this._tempOutputSeriesName, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public double InverseFDistribution(double probability, int firstDegreeOfFreedom, int secondDegreeOfFreedom)
        {
            string parameters = (probability.ToString(CultureInfo.InvariantCulture) + "," + firstDegreeOfFreedom.ToString(CultureInfo.InvariantCulture)) + "," + secondDegreeOfFreedom.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("InverseFDistribution", parameters, this._tempOutputSeriesName, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public double InverseNormalDistribution(double probability)
        {
            string parameters = probability.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("InverseNormalDistribution", parameters, this._tempOutputSeriesName, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public double InverseTDistribution(double probability, int degreeOfFreedom)
        {
            string parameters = probability.ToString(CultureInfo.InvariantCulture) + "," + degreeOfFreedom.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("InverseTDistribution", parameters, this._tempOutputSeriesName, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public double Mean(string inputSeriesName)
        {
            if (inputSeriesName == null)
            {
                throw new ArgumentNullException("inputSeriesName");
            }
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            string inputSeries = inputSeriesName;
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("Mean", "", inputSeries, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public double Median(string inputSeriesName)
        {
            if (inputSeriesName == null)
            {
                throw new ArgumentNullException("inputSeriesName");
            }
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            string inputSeries = inputSeriesName;
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("Median", "", inputSeries, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public double NormalDistribution(double zValue)
        {
            string parameters = zValue.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("NormalDistribution", parameters, this._tempOutputSeriesName, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        private void RemoveEmptyPoints(string seriesName)
        {
            Series series = this._formulaData.Common.DataManager.Series[seriesName];
            for (int i = 0; i < series.Points.Count; i++)
            {
                if (series.Points[i].IsEmpty)
                {
                    series.Points.RemoveAt(i--);
                }
            }
        }

        public double TDistribution(double value, int degreeOfFreedom, bool oneTail)
        {
            string parameters = value.ToString(CultureInfo.InvariantCulture) + "," + degreeOfFreedom.ToString(CultureInfo.InvariantCulture);
            if (oneTail)
            {
                parameters = parameters + ",1";
            }
            else
            {
                parameters = parameters + ",2";
            }
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("TDistribution", parameters, this._tempOutputSeriesName, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public TTestResult TTestEqualVariances(double hypothesizedMeanDifference, double probability, string firstInputSeriesName, string secondInputSeriesName)
        {
            if (firstInputSeriesName == null)
            {
                throw new ArgumentNullException("firstInputSeriesName");
            }
            if (secondInputSeriesName == null)
            {
                throw new ArgumentNullException("secondInputSeriesName");
            }
            TTestResult result = new TTestResult();
            string parameters = hypothesizedMeanDifference.ToString(CultureInfo.InvariantCulture) + "," + probability.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            string inputSeries = firstInputSeriesName.ToString(CultureInfo.InvariantCulture) + "," + secondInputSeriesName.ToString(CultureInfo.InvariantCulture);
            try
            {
                this._formulaData.Formula("TTestEqualVariances", parameters, inputSeries, this._tempOutputSeriesName);
                DataPointCollection points = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points;
                result.firstSeriesMean = points[0].YValues[0];
                result.secondSeriesMean = points[1].YValues[0];
                result.firstSeriesVariance = points[2].YValues[0];
                result.secondSeriesVariance = points[3].YValues[0];
                result.tValue = points[4].YValues[0];
                result.degreeOfFreedom = points[5].YValues[0];
                result.probabilityTOneTail = points[6].YValues[0];
                result.tCriticalValueOneTail = points[7].YValues[0];
                result.probabilityTTwoTail = points[8].YValues[0];
                result.tCriticalValueTwoTail = points[9].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return result;
        }

        public TTestResult TTestPaired(double hypothesizedMeanDifference, double probability, string firstInputSeriesName, string secondInputSeriesName)
        {
            if (firstInputSeriesName == null)
            {
                throw new ArgumentNullException("firstInputSeriesName");
            }
            if (secondInputSeriesName == null)
            {
                throw new ArgumentNullException("secondInputSeriesName");
            }
            TTestResult result = new TTestResult();
            string parameters = hypothesizedMeanDifference.ToString(CultureInfo.InvariantCulture) + "," + probability.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            string inputSeries = firstInputSeriesName.ToString(CultureInfo.InvariantCulture) + "," + secondInputSeriesName.ToString(CultureInfo.InvariantCulture);
            try
            {
                this._formulaData.Formula("TTestPaired", parameters, inputSeries, this._tempOutputSeriesName);
                DataPointCollection points = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points;
                result.firstSeriesMean = points[0].YValues[0];
                result.secondSeriesMean = points[1].YValues[0];
                result.firstSeriesVariance = points[2].YValues[0];
                result.secondSeriesVariance = points[3].YValues[0];
                result.tValue = points[4].YValues[0];
                result.degreeOfFreedom = points[5].YValues[0];
                result.probabilityTOneTail = points[6].YValues[0];
                result.tCriticalValueOneTail = points[7].YValues[0];
                result.probabilityTTwoTail = points[8].YValues[0];
                result.tCriticalValueTwoTail = points[9].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return result;
        }

        public TTestResult TTestUnequalVariances(double hypothesizedMeanDifference, double probability, string firstInputSeriesName, string secondInputSeriesName)
        {
            if (firstInputSeriesName == null)
            {
                throw new ArgumentNullException("firstInputSeriesName");
            }
            if (secondInputSeriesName == null)
            {
                throw new ArgumentNullException("secondInputSeriesName");
            }
            TTestResult result = new TTestResult();
            string parameters = hypothesizedMeanDifference.ToString(CultureInfo.InvariantCulture) + "," + probability.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            try
            {
                string inputSeries = firstInputSeriesName.ToString(CultureInfo.InvariantCulture) + "," + secondInputSeriesName.ToString(CultureInfo.InvariantCulture);
                this._formulaData.Formula("TTestUnequalVariances", parameters, inputSeries, this._tempOutputSeriesName);
                DataPointCollection points = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points;
                result.firstSeriesMean = points[0].YValues[0];
                result.secondSeriesMean = points[1].YValues[0];
                result.firstSeriesVariance = points[2].YValues[0];
                result.secondSeriesVariance = points[3].YValues[0];
                result.tValue = points[4].YValues[0];
                result.degreeOfFreedom = points[5].YValues[0];
                result.probabilityTOneTail = points[6].YValues[0];
                result.tCriticalValueOneTail = points[7].YValues[0];
                result.probabilityTTwoTail = points[8].YValues[0];
                result.tCriticalValueTwoTail = points[9].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return result;
        }

        public double Variance(string inputSeriesName, bool sampleVariance)
        {
            if (inputSeriesName == null)
            {
                throw new ArgumentNullException("inputSeriesName");
            }
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            string inputSeries = inputSeriesName;
            string parameters = sampleVariance.ToString(CultureInfo.InvariantCulture);
            double naN = double.NaN;
            try
            {
                this._formulaData.Formula("Variance", parameters, inputSeries, this._tempOutputSeriesName);
                naN = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points[0].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return naN;
        }

        public ZTestResult ZTest(double hypothesizedMeanDifference, double varianceFirstGroup, double varianceSecondGroup, double probability, string firstInputSeriesName, string secondInputSeriesName)
        {
            if (firstInputSeriesName == null)
            {
                throw new ArgumentNullException("firstInputSeriesName");
            }
            if (secondInputSeriesName == null)
            {
                throw new ArgumentNullException("secondInputSeriesName");
            }
            ZTestResult result = new ZTestResult();
            string parameters = ((hypothesizedMeanDifference.ToString(CultureInfo.InvariantCulture) + "," + varianceFirstGroup.ToString(CultureInfo.InvariantCulture)) + "," + varianceSecondGroup.ToString(CultureInfo.InvariantCulture)) + "," + probability.ToString(CultureInfo.InvariantCulture);
            this._formulaData.Common.DataManager.Series.Add(new Series(this._tempOutputSeriesName));
            string inputSeries = firstInputSeriesName.ToString(CultureInfo.InvariantCulture) + "," + secondInputSeriesName.ToString(CultureInfo.InvariantCulture);
            try
            {
                this._formulaData.Formula("ZTest", parameters, inputSeries, this._tempOutputSeriesName);
                DataPointCollection points = this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName].Points;
                result.firstSeriesMean = points[0].YValues[0];
                result.secondSeriesMean = points[1].YValues[0];
                result.firstSeriesVariance = points[2].YValues[0];
                result.secondSeriesVariance = points[3].YValues[0];
                result.zValue = points[4].YValues[0];
                result.probabilityZOneTail = points[5].YValues[0];
                result.zCriticalValueOneTail = points[6].YValues[0];
                result.probabilityZTwoTail = points[7].YValues[0];
                result.zCriticalValueTwoTail = points[8].YValues[0];
            }
            finally
            {
                this._formulaData.Common.DataManager.Series.Remove(this._formulaData.Common.DataManager.Series[this._tempOutputSeriesName]);
            }
            return result;
        }
    }
}

